package com.tencent.sr.iris.activity.service.event.handler;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.tencent.sr.iris.activity.common.enums.EventHandingRecordStatusEnum;
import com.tencent.sr.iris.activity.dependency.user.UserCoreDependency;
import com.tencent.sr.iris.activity.domain.event.dto.ActivityDTO;
import com.tencent.sr.iris.activity.domain.event.dto.ActivityTaskDTO;
import com.tencent.sr.iris.activity.interfaces.event.dto.ActivityEventDTO;
import com.tencent.sr.iris.activity.repository.entity.TIrisEventHandingRecordDO;
import com.tencent.sr.iris.activity.service.activity.IrisActivityService;
import com.tencent.sr.iris.activity.service.activity.IrisEventHandingRecordService;
import com.tencent.sr.iris.activity.service.config.ActivityConfig;
import com.tencent.sr.iris.activity.service.event.EventExecutorContext;
import com.tencent.sr.iris.activity.service.event.activity.IrisActivityExecutor;
import com.tencent.sr.iris.user.core.service.interfaces.response.activity.SupervisorRelationResponse;
import com.tencent.sr.rmall.common.exception.retail.TRetailBizException;
import com.tencent.sr.rmall.common.exception.retail.TRetailErrorException;
import com.tencent.sr.rmall.springbootstarter.monitor.util.MonitorUtil;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.util.StringUtils;

/**
 * 事件处理模板类
 *
 * @author xiaojun
 */
@Slf4j
public abstract class AbstractIrisEventHandler implements IrisEventHandler {


    @Resource
    IrisEventHandingRecordService irisEventHandingRecordService;
    @Resource
    public ActivityConfig activityConfig;
    @Resource
    IrisActivityService irisActivityService;
    @Resource
    UserCoreDependency userCoreDependency;
    @Resource
    private EventExecutorContext eventExecutorContext;
    @Resource
    private RedissonClient redissonClient;

    /**
     * 需要实现的具体逻辑
     *
     * @param eventDTO
     */
    @Override
    public void execute(ActivityEventDTO eventDTO) throws Exception {
        Throwable throwable = null;
        long startTime = System.currentTimeMillis();
        List<String> eventHandingInfoList = Lists.newArrayList();
        RLock lock = null;
        TIrisEventHandingRecordDO handingRecordDO = null;
        try {
            String lockKey = getLockKey(eventDTO.getIdempotent());
            lock = redissonClient.getLock(lockKey);
            if (!lock.tryLock(30, TimeUnit.SECONDS)) {
                log.info("事件处理：事件获取分布式锁失败，当前事件正在处理，忽略重复推送，idempotent:{}", eventDTO.getIdempotent());
                return;
            }
            handingRecordDO = irisEventHandingRecordService.queryRecord(eventDTO, null, null);
            if (handingRecordDO == null) {
                handingRecordDO = irisEventHandingRecordService.saveHandingRecord(eventDTO, null, null,
                        eventHandingInfoList);
            }
            //事件基础信息校验
            checkEventBaseInfo(eventDTO);

            //前置处理
            before(eventDTO);
            //用户基础数据累加等处理
//            baseDataProcess(eventDTO);
            //处理事件
            process(eventDTO, eventHandingInfoList);
            //后置处理
            after(eventDTO);

            handingRecordDO.setHandingInfo(JSON.toJSONString(eventHandingInfoList));
            handingRecordDO.setHandingStatus(EventHandingRecordStatusEnum.SUCCEES.getCode());
        } catch (TRetailBizException e) {
            //业务异常只做中断，不重试
            eventHandingInfoList.add(e.getMessage());
            handingRecordDO.setHandingInfo(JSON.toJSONString(eventHandingInfoList));
            handingRecordDO.setHandingStatus(EventHandingRecordStatusEnum.SUCCEES.getCode());
            log.info("事件处理业务异常 eventIdempotent:{}", eventDTO.getIdempotent(), e);
        } catch (TRetailErrorException e) {
            //业务错误中断，需要重试
            eventHandingInfoList.add(e.getMessage());
            handingRecordDO.setHandingInfo(JSON.toJSONString(eventHandingInfoList));
            handingRecordDO.setFailCount((byte) (handingRecordDO.getFailCount() + 1));
            handingRecordDO.setHandingStatus(EventHandingRecordStatusEnum.FAIL.getCode());
            throwable = e;
            log.error("事件处理业务错误 eventIdempotent:{}", eventDTO.getIdempotent(), e);
        } catch (Exception e) {
            eventHandingInfoList.add("系统异常");
            if (handingRecordDO == null) {
                log.error("事件处理：结果：系统异常，handingRecordDO为空，不保存处理日志，消息重试,eventIdempotent:{}", eventDTO.getIdempotent(),
                        e);
                throw e;
            }
            handingRecordDO.setHandingInfo(JSON.toJSONString(eventHandingInfoList));
            handingRecordDO.setFailCount((byte) (handingRecordDO.getFailCount() + 1));
            handingRecordDO.setHandingStatus(EventHandingRecordStatusEnum.FAIL.getCode());
            throwable = e;
            log.error("事件处理系统异常 eventIdempotent:{}", eventDTO.getIdempotent(), e);
        } finally {
            if (handingRecordDO != null) {
                irisEventHandingRecordService.updateRecord(handingRecordDO);
            }

            if (lock != null) {
                lock.unlock();
            }
            log.info("事件处理：eventIdempotent:{},耗时：{}", eventDTO.getIdempotent(),
                    (System.currentTimeMillis() - startTime));

            String translationName = "event";
            if (eventDTO != null && !StringUtils.isEmpty(eventDTO.getEventType())) {
                translationName = translationName + "_" + eventDTO.getEventType();
            }
            MonitorUtil.getSingleMonitorService().logTransaction("activity_event_handler", translationName,
                    startTime, throwable);
        }

    }

    /**
     * 获取分布式锁
     *
     * @param eventIdempotent
     * @return
     */
    private String getLockKey(String eventIdempotent) {
        return String.format("irisEventHandler_lock_%s", eventIdempotent);
    }

    /**
     * 校验event的基础信息
     *
     * @param eventDTO
     */
    private void checkEventBaseInfo(ActivityEventDTO eventDTO) {
        if (eventDTO == null) {
            throw new TRetailBizException("事件为空");
        }
        if (StringUtils.isEmpty(eventDTO.getIdempotent())) {
            throw new TRetailBizException("事件idempotent为空");
        }
    }

    /**
     * 事件前置处理，可以做参数校验和补全等
     *
     * @param activityEvent
     */
    private void before(ActivityEventDTO activityEvent) {
        log.info("activity event consume,idempotent={},uid={},eventtype={},eventInfo={}", activityEvent.getIdempotent(),
                activityEvent.getUid(), activityEvent.getEventType(), activityEvent.getEventInfo());
        paramPreProcessing(activityEvent);
        if (StringUtils.isEmpty(activityEvent.getUid())) {
            log.error("uid can not be null");
            throw new TRetailBizException("uid can not be null");
        }
        if (StringUtils.isEmpty(activityEvent.getIdempotent())) {
            log.error("idempotent can not be null");
            throw new TRetailBizException("idempotent can not be null");
        }
        if (activityEvent.getEventTime() == null) {
            log.error("eventTime can not be null");
            throw new TRetailBizException("eventTime can not be null");
        }

        //3月1号零点之前，白名单用户可进 ，零点以后不做控制
        if (activityEvent.getEventTime() < activityConfig.getActRealUserStartTimestamp()) {
            if (!activityConfig.getTestUidWhiteList().contains(activityEvent.getUid())) {
                log.info("不在活动时间段内,不属于白名单用户,uid={},idempotent={}", activityEvent.getUid(),
                        activityEvent.getIdempotent());
                throw new TRetailBizException("不在活动时间段内,不属于白名单用户,uid=" + activityEvent.getUid() + ",idempotent="
                        + activityEvent.getIdempotent());
            }
        }
    }

    /**
     * @param msg
     */
    void paramPreProcessing(ActivityEventDTO msg) {

    }

    /**
     * 事件前置处理
     *
     * @param eventDTO
     */
    public abstract void after(ActivityEventDTO eventDTO);


    /**
     * 业务处理
     *
     * @param eventDTO
     * @param eventHandingInfoList
     */
    void process(ActivityEventDTO eventDTO, List<String> eventHandingInfoList) {
        //查找事件关联的任务
        List<ActivityTaskDTO> allTaskList = getRelatedTaskList(eventDTO.getEventType());
        if (CollectionUtil.isEmpty(allTaskList)) {
            log.info("事件类型:{}未关联任务，忽略事件", eventDTO.getEventType());
            //记录事件处理日志
            eventHandingInfoList.add("未关联任务，忽略事件");
            return;
        }

        Map<String, List<ActivityTaskDTO>> actIdTaskMap = allTaskList.stream()
                .collect(Collectors.groupingBy(ActivityTaskDTO::getActivityId));
        //获取任务所在活动
        actIdTaskMap.entrySet().stream().forEach(entry -> {
            try {
                String actId = entry.getKey();
                List<ActivityTaskDTO> actTaskList = entry.getValue();
                ActivityDTO activityDTO = getActById(actId);
                activityDTO.setActivityTaskDTOS(actTaskList);
                //执行活动处理器
                IrisActivityExecutor activityExecutor = getActivityExecutor(eventDTO, activityDTO);
                activityExecutor.execute(eventDTO, activityDTO);
                eventHandingInfoList.add(this.getClass().getSimpleName() + "处理成功,actId:" + actId);
            } catch (Exception e) {
                log.error(this.getClass().getSimpleName() + "处理异常,entry:{}", JSON.toJSONString(entry), e);
                eventHandingInfoList.add(this.getClass().getSimpleName() + "处理异常");
            }

        });
    }


    /**
     * 获取事件类型对应的活动处理器
     *
     * @param activityDTO
     * @return
     */
    protected IrisActivityExecutor getActivityExecutor(ActivityEventDTO eventDTO, ActivityDTO activityDTO) {
        return eventExecutorContext.getActivityHandler(activityDTO.getBizId());
    }


    /**
     * 根据活动bizId查询活动对象
     *
     * @param actId
     * @return
     */
    public ActivityDTO getActById(String actId) {
        return irisActivityService.getActivity(actId);
    }


    /**
     * 查找事件关联的任务列表
     *
     * @param eventType
     * @return
     */
    abstract List<ActivityTaskDTO> getRelatedTaskList(String eventType);

    /**
     * 注意：需要做幂等
     * 收到事件后记录用户事件信息
     *
     * @param eventDTO
     */
    abstract void baseDataProcess(ActivityEventDTO eventDTO);


    protected void addParameterForEvent(ActivityEventDTO eventDTO) {
        //补充pid
        try {
            SupervisorRelationResponse response = userCoreDependency
                    .queryUserSupervisor(eventDTO.getUid());
            if (Objects.nonNull(response) && !StringUtils.isEmpty(response.getParentUid())) {
                eventDTO.setPid(response.getParentUid());
            }
        } catch (Exception e) {
            log.info("查询上级pid失败，uid:{}", eventDTO.getUid());
            throw new TRetailErrorException("事件补充用户PID失败");
        }
    }
}
